﻿using System.Collections.Generic;
using UnityEngine;

public sealed class Scheduler {

    public static readonly Scheduler Instance = new Scheduler();

    public delegate void OnScheduler();

    private class FrameScheduler
    {
        public uint ID = 0;
        public uint Frame = 0;
        public uint RealFrame = 0;
        public bool IsLoop = false;
        public OnScheduler Callback = null;
    }
    private List<FrameScheduler> _frameDelegates;

    private class TimeScheduler
    {
        public uint ID =0;
        public float RealTime = 0.0F;
        public float Time=0.0f;
        public bool IsLoop =false;
        public OnScheduler Callback = null;
    }
	private List<TimeScheduler> _timeSchedulers;

	private List<OnScheduler> _updateScheduler;

    private uint _curFrame;

    private uint _curAllotID;

    /// <summary>
    /// 构造
    /// </summary>
    private Scheduler()
    {
        _curFrame = 0;
        _curAllotID = 0;
        _frameDelegates = new List<FrameScheduler>();
        _timeSchedulers = new List<TimeScheduler>();
		_updateScheduler = new List<OnScheduler> ();
    }

    ~Scheduler()
    {
        _frameDelegates.Clear();
        _frameDelegates = null;
        _timeSchedulers.Clear();
        _timeSchedulers = null;
		_updateScheduler.Clear();
		_updateScheduler = null;
    }

    /// <summary>
    /// 更新
    /// </summary>
    public void Update()
    {
        ++_curFrame;

        UpdateFrameScheduler();

        UpdateTimeScheduler();

		UpdateUpdator();
    }

	private void UpdateUpdator()
	{
		if(_updateScheduler.Count <= 0)
		{
			return;
		}

		for (var i=0; i < _updateScheduler.Count; ++i) 
		{
			_updateScheduler[i]();
		}
	}

    private void UpdateFrameScheduler()
    {
		for (int i= _frameDelegates.Count-1; i >= 0;i-- ) 
		{
            if (i >= _frameDelegates.Count)
            {
                continue;
            }
            FrameScheduler obj = _frameDelegates[i];
			if (obj.RealFrame <= _curFrame)
			{
				obj.Callback();
				if (obj.IsLoop)
				{
					obj.RealFrame += obj.Frame;
				}
				else
				{
					_frameDelegates.Remove(obj);
					continue;
				}
			}
		}
    }

    private void UpdateTimeScheduler()
	{
		for (var i= _timeSchedulers.Count-1; i >= 0;i-- ) 
		{
            if (i >= _timeSchedulers.Count)
            {
                continue;
            }
            TimeScheduler obj = _timeSchedulers[i];
			if (obj.RealTime <= Time.time)
			{
				obj.Callback();
				if (obj.IsLoop)
				{
					obj.RealTime += obj.Time;
				}
				else
				{
					_timeSchedulers.Remove(obj);
					continue;
				}
			}
		}

    }

    public void AddFrame(uint frame, bool loop, OnScheduler callback)
    {
        ++_curAllotID;
        var frameScheduler = new FrameScheduler
        {
            ID = _curAllotID,
            Frame = frame,
            RealFrame = frame + _curFrame,
            IsLoop = loop,
            Callback = callback
        };
        _frameDelegates.Add(frameScheduler);
    }

	public void RemoveFrame(OnScheduler callback)
    {
		for (var i=0; i<_frameDelegates.Count; ++i) 
		{
			var deleData = _frameDelegates[i];
			if(deleData.Callback == callback)
			{
				_frameDelegates.RemoveAt(i);
				break;
			}
		}
    }

    public void AddTimer(float time, bool loop, OnScheduler callback)
    {
        if (IsAddTimer(time, loop, callback))
        {
            return;
        }
        ++_curAllotID;
        var timeScheduler = new TimeScheduler
        {
            ID = _curAllotID,
            Time = time,
            RealTime = time + Time.time,
            IsLoop = loop,
            Callback = callback
        };
        _timeSchedulers.Add(timeScheduler);
    }


    public bool IsAddTimer(float time, bool loop, OnScheduler callback) 
    {
        for (var i = 0; i < _timeSchedulers.Count; ++i)
        {
            var deleData = _timeSchedulers[i];
            if (deleData.Callback == callback && time == deleData.Time && loop == deleData.IsLoop)
            {
                return true;
            }
        }
        return false;
    }
	public void RemoveTimer(OnScheduler callback)
    {
        if (callback == null) return;
		for (var i=0; i<_timeSchedulers.Count; ++i) 
		{
			var deleData = _timeSchedulers[i];
			if(deleData.Callback == callback)
			{
				_timeSchedulers.RemoveAt(i);
				break;
			}
		}
    }

	public void AddUpdator(OnScheduler callback)
	{
		_updateScheduler.Add(callback);
	}

	public void RemoveUpdator(OnScheduler callback)
	{
		_updateScheduler.Remove(callback);
	}
}
